#!/usr/bin/python

import sys
import datetime
from pyodm.document import Document
from pyodm.fields import *
from pyodm.criteria import Criteria

class Bid(Document):
    bid = IntegerField()
    date = DateField()
    user = StringField()

class Auction(Document):
    name = StringField(unique=True, index=True)
    enddate = DateField(index=True)
    closed = BoolField()
    lastbid = IntegerField()
    lastbid_date = DateField()
    lastbid_user = StringField()
    bids = ReferenceField(Bid)

def usage():
    """Print out in case of improper usage"""
    sys.stderr.write("""
Usage:
\t%s showbids <auction_name>
\t%s bid <auction_name> <user> <bid_value>
\t%s newauction <auction_name> <bid_startvalue> <enddate>
\t%s closeauction <auction_name>

""" % (sys.argv[0], sys.argv[0], sys.argv[0], sys.argv[0]))

    sys.exit(1)

def closeauction(auction_name):
    """Closes the auction, and calculates the winner according to the bid-log"""
    now = datetime.datetime.utcnow()

    try:
        auction = Auction.get([Criteria("name", "=", auction_name)])[0]
    except IndexError:
        raise Exception("No such auction found")

    # Checks whether we can close the auction (enddate must be in the past)
    if auction.enddate < now:

        bids = auction.bids.get()
        winner_bid = None

        # Find a maximal bid (by its value and date => last highest bid)
        for bid in bids:
            if winner_bid is None:
                winner_bid = bid
            else:
                if bid.bid > winner_bid.bid and bid.date > winner_bid.date:
                    winner_bid = bid

        # If there's no bid, use the default startbid, and dummy user
        if winner_bid is not None:
            auction.lastbid = winner_bid.bid
            auction.lastbid_user = winner_bid.user
            auction.lastbid_date = winner_bid.date
            auction.closed = True
            auction.save()

    else:
        print "Cannot close auction yet, enddate is in the future"

def getlastbid(auction_name):
    """Gets and prints lastbid information of an auction"""

    # Find the auction
    try:
        auction = Auction.get([Criteria("name", "=", auction_name)])[0]
    except IndexError:
        raise Exception("No such auction found")

    # Print out the lastbid, and some other useful info
    print """
Auction status
--------------

Auction name: %s
End date: %s
Closed: %s
Last bid: %s
Last bid user: %s
Last bid date %s
""" % (auction.name, auction.enddate, auction.closed, auction.lastbid,
       auction.lastbid_user, auction.lastbid_date)

def showbids(auction_name):
    """Gets and prints every bids for a given auction"""

    # Find the auction
    try:
        auction = Auction.get([Criteria("name", "=", auction_name)])[0]
    except IndexError:
        raise Exception("No such auction found")
    
    # Get bids
    bids = auction.bids.get()
    
    # Print out the lastbid, and some other useful info
    print """
Auction status
--------------

Auction name: %s
End date: %s
Closed: %s
Last bid: %s
Last bid user: %s
Last bid date %s
""" % (auction.name, auction.enddate, auction.closed, auction.lastbid,
       auction.lastbid_user, auction.lastbid_date)
       
    print "Bids:"
    for bid in bids:
        print "%s : %s @ %s" % (bid.user, bid.bid, bid.date)
    print

def newauction(auction_name, startvalue, enddate):
    """Creates a new record in auction collection"""

    # Auction properties
    now = datetime.datetime.utcnow()
    auction = Auction(
        name=auction_name,
        enddate=enddate,
        lastbid=startvalue,
        lastbid_date=now,
        lastbid_user='dummy',
        closed=False
    )

    auction.save()

def bid(auction_name, user, value):
    """Makes a new bid in an auction, if it's a valid bid"""

    # Getting the auction
    try:
        auction = Auction.get([Criteria("name", "=", auction_name)])[0]
    except IndexError:
        raise Exception("No such auction found")

    # Checks whether the auction weren't closed
    now = datetime.datetime.utcnow()
    if auction.enddate > now:
        # Checks validity of bid by the lastbid information (newer and higher
        # than lastbid)
        if value > auction.lastbid and now > auction.lastbid_date:
            # Updating lastbid info (last-write-wins)
            auction.lastbid_user = user
            auction.lastbid_date = now
            auction.lastbid = value

            # Insert new bid into bid-log
            bid = Bid(user=user, date=now, bid=value)

            auction.bids.add(bid)
            bid.save()
            auction.save()

    else:
        print "Auction has already finished"

if __name__ == "__main__":
    if len(sys.argv) < 3:
        usage()

    cmd = sys.argv[1].lower().strip()
    auction_name = sys.argv[2].lower().strip()

    if cmd == "getlastbid":
        getlastbid(auction_name)
    elif cmd == "bid":
        try:
            user = sys.argv[3].lower().strip()
            value = int(sys.argv[4].lower().strip())
        except:
            usage()
        bid(auction_name, user, value)
    elif cmd == "newauction":
        try:
            startvalue = int(sys.argv[3].lower().strip())
            enddate = datetime.datetime.strptime(sys.argv[4].lower().strip(),
                                                 "%Y%m%d-%H%M")
        except:
            sys.stderr.write("Cannot parse input arguments\n")
            usage()
        newauction(auction_name, startvalue, enddate)
    elif cmd == "closeauction":
        closeauction(auction_name)
    elif cmd == "showbids":
        showbids(auction_name)
    else:
        usage()

